<?php
declare (strict_types = 1);

namespace util;

use Exception;
use exception\FriendlyException;
use Phinx\Db\Adapter\MysqlAdapter;
use Phinx\Db\Table\Column;
use Phinx\Migration\AbstractMigration;

class Migration extends AbstractMigration {

    protected $allowColumns = ['varchar', 'text', 'integer', 'float', 'decimal', 'datetime', 'timestamp', 'time', 'date', 'binary', 'boolean'];

    public static function build($options) {
        $migration = new Migration([], '');
        $adapter = new MysqlAdapter([
            "host" => $options['hostname'],
            "port" => $options['hostport'],
            "name" => $options['database'],
            "user" => $options['username'],
            "pass" => $options['password'],
        ]);
        $adapter->connect();
        $migration->setAdapter($adapter);
        return $migration;
    }

    public function getTableInfo($modelClass) {
        $parser = ClassAttrParser::parser($modelClass);
        $table = $parser->getMetaAttr('Table');
        $comment = $parser->getMetaAttr('Comment');
        $columns = $parser->getMetaAttr('Column', []);
        if(empty($columns)) throw new FriendlyException("表${table}无字段");
        $res = $this->parseModelColumns($columns);
        return [
            'class' => $modelClass,
            'name' => $table,
            'comment' => $comment,
            'primary_keys' => $res['primary_keys'],
            'columns' => $res['columns'],
            'indexs' => $res['indexs'],
        ];
    }

    private function parseModelColumns($columnOptions) {
        $columns =[];
        $indexs = [];
        $primaryKeys = [];
        foreach ($columnOptions as $options) {
            $isUnique = false;
            $column = new Column();
            $column->setName($options['name']);
            $column->setCollation('utf8mb4_general_ci');
            // type
            if(!isset($options['type'])) throw new Exception("type必填");
            $type = $options['type'];
            if(!in_array($type, $this->allowColumns)) throw new Exception("不允许的数据类型: ". $type);
            if($type == 'varchar') $type = 'string';
            $column->setType($type);
            // primary
            if(isset($options['pk'])) {
                // 主键
                $primaryKeys[] = $column->getName();
                if($type != 'string') {
                    $column->setIdentity(true);
                }
            }
            //
            if(isset($options['unsigned'])) $column->setSigned(false);
            //
            if(isset($options['auto'])) $column->setIncrement(true);
            //
            if(isset($options['nullable'])) $column->setNull(true);
            //
            if(isset($options['length'])) $column->setLimit($options['length']);
            if(isset($options['comment'])) $column->setComment($options['comment']);
            if(isset($options['default'])) $column->setDefault($options['default']);
            // 默认值
            if($column->getDefault() == null) {
                if(in_array($column->getType(), ['string'])) $column->setDefault('');
                if(in_array($column->getType(), ['integer', 'boolean', 'float', 'decimal'])) {
                    if(!$column->isIdentity()) $column->setDefault(0);
                }
            }
            // 唯一索引
            if($isUnique) {
                $indexs[] = [ 'columns' => $column->getName(), 'options' => ['unique' => true ] ];
            }
            $columns[] = $column;
        }
        return  [
            'primary_keys' => $primaryKeys,
            'columns' => $columns,
            'indexs' => $indexs,
        ];
    }

    public function hasDatabase($name) {
        return $this->getAdapter()->hasDatabase($name);
    }

    public function useDatabase($name) {
        return $this->getAdapter()->execute(sprintf('USE DATABASE `%s`', $name));
    }

    public function dropDatabase($name) {
        parent::dropDatabase($name);
    }

    public function createDatabase($name, $options = []) {
        parent::createDatabase($name, [
            'charset' => 'utf8mb4',
            'collation' => 'utf8mb4_general_ci',
        ]);
    }

    public function recreateTable($table) {
        $this->dropTable($table['name']);
        $this->createTable($table);
    }

    public function dropTable($tableName) {
        $table = $this->table($tableName);
        if($table->exists()) {
            $this->table($tableName)->drop()->save();
        }
    }

    public function createTable($tableInfo) {
        $options = [
            'id' => false, // 不自动生成ID
            'charset' => 'utf8mb4',
            'collation' => 'utf8mb4_general_ci',
            'comment' => $tableInfo['comment'] ?? '',
        ];
        if(!empty($tableInfo['primary_keys'])) {
            $options['primary_key'] = $tableInfo['primary_keys'];
        }
        $table = $this->table($tableInfo['name'], $options);
        foreach ($tableInfo['columns'] as $column) {
            $table->addColumn($column);
        }
        foreach ($tableInfo['indexs'] as $index) {
            $columns = $index['columns'];
            $options = array_merge($index['options'], []);
            $table->addIndex($columns, $options);
        }
        $table->create();
    }

    public function dropSchemaTable() {
        /**
         * @var AbstractAdapter
         */
        $adapter = $this->getAdapter();
        $name = $adapter->getSchemaTableName();
        $this->dropTable($name);
    }

    public function seed($tableInfo, $data) {
        $this->table($tableInfo['name'])->insert($data)->saveData();
    }
}
